package com.visualexpress.predict;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import com.visualexpress.dao.LocationAvertimeDAO;
import com.visualexpress.dao.LocationPairShipmentDAO;
import com.visualexpress.dao.RouteInfoDAO;
import com.visualexpress.dao.TimeLocationDAO;
import com.visualexpress.domain.Location;
import com.visualexpress.domain.LocationAvertime;
import com.visualexpress.domain.LocationPairShipment;
import com.visualexpress.domain.RouteInfo;
import com.visualexpress.domain.Shipment;
import com.visualexpress.domain.TimeLocation;
import com.visualexpress.util.DateUtil;

public class PredictForShipment {
	/**
	 * I assume that the shipment for this method have state = false
	 * @param shipment
	 * @return
	 */
	public static Date predictArrivalTime(Shipment shipment) {
		Location dest = shipment.getEndLocation();
		TimeLocationDAO timeLocDAO = new TimeLocationDAO();
		LocationAvertimeDAO locAverTimeDAO = new LocationAvertimeDAO();
		List<TimeLocation> timeLocationList = (List<TimeLocation>) timeLocDAO
				.findByShipment(shipment);
		Collections.sort(timeLocationList, new Comparator<TimeLocation>() {
			public int compare(TimeLocation o1, TimeLocation o2) {
				return o1.getLocationNumber() - o2.getLocationNumber();
			}
		});
		TimeLocation timeLocLastCity = timeLocationList.get(timeLocationList.size() - 1);
		Location lastCity = timeLocLastCity.getLocation();
		LocationAvertime locAverTimeLastCity = locAverTimeDAO.findByLocation(lastCity);
		Date leaveTime = timeLocLastCity.getLeaveTime();
		if(leaveTime == null) {
			//the shipment is at the city
			Date timeToLeave =  DateUtil.addHours(timeLocLastCity.getArrivalTime(), locAverTimeLastCity.getTimeStay());
			Date timeNow = DateUtil.getSystemTime();
			if(timeToLeave.before(timeNow)) {
				timeToLeave = timeNow;
			}
			double meanTravelTime = MeanTimeBetweenCities.meanTimeBetweenCitiesInHours(lastCity, dest);
			if(meanTravelTime == -1.0) {
				return null;
			}
			Date arrivalTime = DateUtil.addHours(timeToLeave,
					meanTravelTime - locAverTimeLastCity.getTimeStay());
			return arrivalTime;
		} else {
			//the shipment is on the way

			double predOnTheWay = predictForOnTheWay(lastCity, dest, leaveTime);
			if (predOnTheWay == -1.0) {
				return null;
			}
			Date arrivalTime = DateUtil.addHours(DateUtil.getSystemTime(),
					predOnTheWay);

			return arrivalTime;
		}
	}
	
	private static double predictForOnTheWay(Location lastCity, Location dest, Date leaveTime) {
		LocationPairShipmentDAO locPairDAO = new LocationPairShipmentDAO();
		TimeLocationDAO timeLocDAO = new TimeLocationDAO();
		RouteInfoDAO routeInfoDAO = new RouteInfoDAO();
		List<LocationPairShipment> lstShipment = locPairDAO.findByLocPair(
				lastCity, dest);
		Location lastNext;
		int total = 0;
		double timeForAverage = 0;
		for (LocationPairShipment locPairShipment : lstShipment) {
			List<TimeLocation> timeLocationList = (List<TimeLocation>) timeLocDAO
					.findByShipment(locPairShipment.getShipment());
			Collections.sort(timeLocationList, new Comparator<TimeLocation>() {
				public int compare(TimeLocation o1, TimeLocation o2) {
					return o1.getLocationNumber() - o2.getLocationNumber();
				}
			});

			int i, loc1Ind = -1;
			for (i = 0; i < timeLocationList.size(); i++) {
				if (timeLocationList.get(i).getLocation().equals(lastCity)) {
					loc1Ind = i;
					break;
				}
			}
			if (loc1Ind != -1) {
				TimeLocation timeLocLastCity = timeLocationList.get(loc1Ind);
				lastNext = timeLocationList.get(loc1Ind + 1).getLocation();
				List<RouteInfo> lstRouteInfo = routeInfoDAO.findByStartAndEnd(
						lastCity, lastNext);
				if (!lstRouteInfo.isEmpty()) {

					Date timeArrivalAtLastNext = DateUtil.addHours(
							leaveTime, lstRouteInfo.get(0)
									.getTime());
					double timeForLastNext;
					Date dateNow = DateUtil.getSystemTime();
					if (timeArrivalAtLastNext.before(dateNow)) {
						timeForLastNext = 0.0;
					} else {
						timeForLastNext = DateUtil.substractInHours(
								timeArrivalAtLastNext, dateNow);
					}

					double hoursLastNextToDest = MeanTimeBetweenCities.meanTimeBetweenCitiesInHours(lastNext, dest);
					
					if (hoursLastNextToDest >= 0) {
						timeForAverage += timeForLastNext + hoursLastNextToDest;
						total++;
					}
				}
			}
		} // for
		
		if(total == 0) {
			return -1.0;
		} else {
			return timeForAverage / total;
		}

	}
	
}
